/**
 * @file myrxtx.c
 * Betriebssysteme - Beispiel 3
 *
 * @author Christian Lachner <ic11b010@technikum-wien.at>
 * @author Max Paflik <ic11b006@technikum-wien.at>
 * @date 2012/06/03
 *
 * @version $Revision: 18 $
 *
 * URL: $HeadURL$
 *
 * Last Modified: $Author: Chris $
 */

#include "myrxtx.h"

char * thisfname = NULL;

/**
*
* \brief Sets Checks given arguments and returns a parsed int from mandatory argument '-m'
* 
* If the mandatory argument '-m' is missing or it's parameter is not in
* the positive range of an unsigned integer the function returns MYRXTX_ERROR.
* 
* \param argc The amount of arguments
* \param argv The Pointers to argument-strings
*
* \return int
*/
int handle_params(int argc, char * argv[]) {
	char c;
	long int result = MYRXTX_ERROR;
	char * endptr;

	/* set thisfname to the calling program's name (argv[0]) */
	thisfname = argv[0];

	/* use getopt to handle the given arguments */
	while((c = getopt(argc, argv, "m:M:")) != -1) {
		switch(c) {
			case 'M':
			case 'm':
				result = strtol(optarg, &endptr, 10);

				/* we make sure that the result was parsed successfully
				 * and it's value is in the range of int and greater
				 * than 0 */
				if(*endptr != '\0' || errno != 0 || result <= 0 || result > INT_MAX) {
					/* errno fails suspiciously often in strtol... */
					print_error_msg("Invalid parameter given for argument '-m'");
					return MYRXTX_ERROR;
				}
				break;
			default:
				/* we always fail when invalid arguments are given */
				return MYRXTX_ERROR;
				break;
		}
	}

	/* check for unhandled arguments and fail in case of a find */
	if(optind < argc) {
		print_error_msg("Invalid argument found!");
		return MYRXTX_ERROR;
	}

	return result;
}

/**
*
* \brief Prints usage-information.
*
* \return void
*/
void print_usage() {
	printf("\nUsage: %s -m <bufsize>\n\n", thisfname);
}

/**
*
* \brief Elaborates errormessages by eno and calls print_error_msg
*
* \param eno The error-number that which's message-string is being generated.
*
* \return void
*/
void print_error_eno(int eno) {
	print_error_msg(strerror(eno));
}

/**
*
* \brief Prints error messages in a standardized format
*
* \param msg The message that is being printed.
*
* \return void
*/
void print_error_msg(char * msg) {
	fprintf(stderr, "%s: %s\n", thisfname, msg);
}

/**
*
* \brief Converts the given parameter challenge an integer
*
* \param challenge The challenge that is being converted.
*
* \return int
*/
int convert_strtol(const char * challenge) {
	int result = 0;
	char ** endptr = NULL;

	if(challenge == NULL) {
		return 0;
	}

	errno = 0;
	result = strtol(challenge, endptr, 10);

	if(endptr == '\0' && errno == 0) {
		return result;
	} else {
		if(errno != 0) {
			print_error_eno(errno);
		}

		return 0;
	}
}

/**
*
* \brief Takes care of the initialization process for a semaphore in a race-secure way.
*
* \param key The unique Semaphore-Key.
* \param initval The initial value of the semaphore.
*
* \return int
*/
int init_sem(key_t key, int initval) {
	int semid;

	errno = 0;
	if((semid = seminit(key, PERMMASK_SEM, initval)) == -1) {
		if(errno == EEXIST) {
			errno = 0;
			if((semid = semgrab(key)) == -1) {
				print_error_eno(errno);
				return MYRXTX_ERROR;
			}
		} else {
			print_error_eno(errno);
			return MYRXTX_ERROR;
		}
	}

#if DEBUG > 0
	fprintf(stderr, "DEBUG: %s: Semaphore ready. id: %i\n", thisfname, semid);
#endif

	return semid;
}

/**
*
* \brief Takes care of the initialization process for a shared memory.
*
* \param key The unique Shared-Memory-Key.
* \param perm The size of the ShM.
*
* \return int
*/
int init_shm(key_t key, size_t size) {
	int shmid = MYRXTX_ERROR;

	errno = 0;
	if((shmid = shmget(key, size * sizeof(int), PERMMASK_SHM|IPC_CREAT)) == -1) {
		print_error_eno(errno);
		return MYRXTX_ERROR;
	}

#if DEBUG > 0
	fprintf(stderr, "DEBUG: %s: ShM ready. id: %i\n", thisfname, shmid);
#endif

	return shmid;
}

/**
*
* \brief Cleans up Semaphores and SHM in case of program termination
*
* \param semid_r The reading semaphore ID
* \param semid_w The writing semaphore ID
* \param shmid The Shared Memory ID
* \param shmptr Pointer to shared memory segment
*/
void cleanup(int * semid_r, int * semid_w, int * shmid, int * shmptr)
{
	/* detach shared memory segments */
	if(shmptr != NULL) {
		if(shmdt(shmptr) == -1) {
			print_error_eno(errno);
		} else {
#if DEBUG > 0
			fprintf(stderr, "DEBUG: %s: ShM detached. id: %i\n", thisfname, *shmid);
#endif
			shmptr = NULL;
		}
	}

	if(shmid != NULL && *shmid != -1) {
		if ((shmctl(*shmid, IPC_RMID, NULL)) == -1) {
			print_error_msg("Could not remove shared memory");
		} else {
#if DEBUG > 0
			fprintf(stderr, "DEBUG: %s: Removed ShM. id: %i\n", thisfname, *shmid);
#endif
			*shmid = -1;
		}
	}

	if(semid_w != NULL && *semid_w != -1) {
		if(semrm(*semid_w) == -1) {
			print_error_msg("Could not remove writing semaphore!");
		} else {
#if DEBUG > 0
			fprintf(stderr, "DEBUG: %s: Removed writing semaphore. id: %i\n", thisfname, *semid_w);
#endif
			*semid_w = -1;
		}
	}

	if(semid_r != NULL && *semid_r != -1) {
		if(semrm(*semid_r) == -1) {
			print_error_msg("Could not remove reading semaphore");
		} else {
#if DEBUG > 0
			fprintf(stderr, "DEBUG: %s: Removed reading semaphore. id: %i\n", thisfname, *semid_r);
#endif
			*semid_r = -1;
		}
	}
}
